<?php
declare (strict_types=1);

namespace app\service;

use think\facade\Log;
use think\facade\Config;

/**
 * 图片压缩服务类
 * 在保证图片质量的前提下，压缩图片大小以适应微信接口要求
 */
class ImageCompressService
{
    /**
     * 配置信息
     */
    private $config;
    
    public function __construct()
    {
        $this->config = Config::get('image_compress', []);
        $this->initDefaultConfig();
    }
    
    /**
     * 初始化默认配置
     */
    private function initDefaultConfig()
    {
        $defaults = [
            'enable' => true,
            'wechat_max_size' => 2 * 1024 * 1024,
            'target_size' => 1.5 * 1024 * 1024,
            'max_width' => 1920,
            'max_height' => 1920,
            'default_quality' => 85,
            'min_quality' => 30,
            'quality_step' => 5,
            'supported_formats' => ['jpg', 'jpeg', 'png', 'gif', 'webp'],
            'quality_strategy' => [
                5 * 1024 * 1024 => 70,
                3 * 1024 * 1024 => 75,
                1 * 1024 * 1024 => 80,
                0 => 85
            ],
            'dimension_strategy' => [
                3000 => [1600, 1600],
                2500 => [1800, 1800],
                0 => [1920, 1920]
            ],
            'log' => [
                'enable' => true,
                'level' => 'info'
            ]
        ];
        
        $this->config = array_merge($defaults, $this->config);
    }
    
    /**
     * 压缩图片
     * 
     * @param string $sourcePath 源图片路径
     * @param string $targetPath 目标图片路径（可选，不传则覆盖源文件）
     * @param int $quality 压缩质量 (1-100)
     * @param int $maxWidth 最大宽度
     * @param int $maxHeight 最大高度
     * @return array 返回压缩结果信息
     */
    public function compressImage(
        string $sourcePath, 
        string $targetPath = null, 
        int $quality = null, 
        int $maxWidth = null, 
        int $maxHeight = null,
        string $ext = null
    ): array {
        try {
            // 检查是否启用压缩
            if (!$this->config['enable']) {
                return [
                    'success' => true,
                    'compressed' => false,
                    'message' => '图片压缩功能已禁用',
                    'path' => $sourcePath
                ];
            }
            
            // 使用配置中的默认值
            $quality = $quality ?? $this->config['default_quality'];
            $maxWidth = $maxWidth ?? $this->config['max_width'];
            $maxHeight = $maxHeight ?? $this->config['max_height'];
            
            // 如果没有指定目标路径，则覆盖源文件
            if ($targetPath === null) {
                $targetPath = $sourcePath;
            }
            
            // 获取原始图片信息
            $originalInfo = $this->getImageInfo($sourcePath);
            if (!$originalInfo) {
                throw new \Exception('无法获取图片信息');
            }
            
            // 检查图片格式是否支持
            if (empty($ext)){
                $ext = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION));
            }
            if (!in_array($ext, $this->config['supported_formats'])) {
                return [
                    'success' => false,
                    'error' => '不支持的图片格式: ' . $ext,
                    'path' => $sourcePath
                ];
            }
            
            // 如果图片已经符合要求，直接返回
            if ($originalInfo['size'] <= $this->config['target_size'] && 
                $originalInfo['width'] <= $maxWidth && 
                $originalInfo['height'] <= $maxHeight) {
                
                $this->log('图片无需压缩', [
                    'path' => $sourcePath,
                    'size' => $originalInfo['size'],
                    'dimensions' => $originalInfo['width'] . 'x' . $originalInfo['height']
                ]);
                
                return [
                    'success' => true,
                    'compressed' => false,
                    'original_size' => $originalInfo['size'],
                    'final_size' => $originalInfo['size'],
                    'compression_ratio' => 0,
                    'path' => $targetPath
                ];
            }
            
            // 创建图片资源
            $sourceImage = $this->createImageResource($sourcePath, $originalInfo['type']);
            if (!$sourceImage) {
                throw new \Exception('无法创建图片资源');
            }
            
            // 计算新的尺寸
            $newDimensions = $this->calculateNewDimensions(
                $originalInfo['width'], 
                $originalInfo['height'], 
                $maxWidth, 
                $maxHeight
            );
            
            // 创建新的图片资源
            $targetImage = imagecreatetruecolor($newDimensions['width'], $newDimensions['height']);
            
            // 保持透明度（针对PNG和GIF）
            if ($originalInfo['type'] === IMAGETYPE_PNG || $originalInfo['type'] === IMAGETYPE_GIF) {
                imagealphablending($targetImage, false);
                imagesavealpha($targetImage, true);
                $transparent = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
                imagefill($targetImage, 0, 0, $transparent);
            }
            
            // 重新采样图片
            imagecopyresampled(
                $targetImage, $sourceImage,
                0, 0, 0, 0,
                $newDimensions['width'], $newDimensions['height'],
                $originalInfo['width'], $originalInfo['height']
            );
            
            // 多次尝试压缩，直到达到目标大小
            $finalQuality = $this->findOptimalQuality(
                $targetImage, 
                $originalInfo['type'], 
                $targetPath, 
                $quality
            );
            
            // 保存最终图片
            $success = $this->saveImage($targetImage, $originalInfo['type'], $targetPath, $finalQuality);
            
            // 释放内存
            imagedestroy($sourceImage);
            imagedestroy($targetImage);
            
            if (!$success) {
                throw new \Exception('保存压缩图片失败');
            }
            
            // 获取压缩后的文件大小
            $finalSize = filesize($targetPath);
            $compressionRatio = round((($originalInfo['size'] - $finalSize) / $originalInfo['size']) * 100, 2);
            
            $this->log('图片压缩完成', [
                'path' => $sourcePath,
                'original_size' => $originalInfo['size'],
                'final_size' => $finalSize,
                'compression_ratio' => $compressionRatio . '%',
                'quality' => $finalQuality,
                'dimensions' => $newDimensions['width'] . 'x' . $newDimensions['height']
            ]);
            
            return [
                'success' => true,
                'compressed' => true,
                'original_size' => $originalInfo['size'],
                'final_size' => $finalSize,
                'compression_ratio' => $compressionRatio,
                'quality' => $finalQuality,
                'path' => $targetPath,
                'dimensions' => $newDimensions
            ];
            
        } catch (\Exception $e) {
            $this->log('图片压缩失败', [
                'path' => $sourcePath,
                'error' => $e->getMessage()
            ], 'error');
            
            return [
                'success' => false,
                'error' => $e->getMessage(),
                'path' => $sourcePath
            ];
        }
    }
    
    /**
     * 获取图片信息
     */
    private function getImageInfo(string $path): ?array
    {
        if (!file_exists($path)) {
            return null;
        }
        
        $imageInfo = getimagesize($path);
        if (!$imageInfo) {
            return null;
        }
        
        return [
            'width' => $imageInfo[0],
            'height' => $imageInfo[1],
            'type' => $imageInfo[2],
            'mime' => $imageInfo['mime'],
            'size' => filesize($path)
        ];
    }
    
    /**
     * 创建图片资源
     */
    private function createImageResource(string $path, int $type)
    {
        switch ($type) {
            case IMAGETYPE_JPEG:
                return imagecreatefromjpeg($path);
            case IMAGETYPE_PNG:
                return imagecreatefrompng($path);
            case IMAGETYPE_GIF:
                return imagecreatefromgif($path);
            case IMAGETYPE_WEBP:
                return imagecreatefromwebp($path);
            default:
                return false;
        }
    }
    
    /**
     * 计算新的图片尺寸（保持宽高比）
     */
    private function calculateNewDimensions(int $width, int $height, int $maxWidth, int $maxHeight): array
    {
        // 如果图片尺寸已经符合要求，不需要缩放
        if ($width <= $maxWidth && $height <= $maxHeight) {
            return ['width' => $width, 'height' => $height];
        }
        
        // 计算缩放比例
        $widthRatio = $maxWidth / $width;
        $heightRatio = $maxHeight / $height;
        $ratio = min($widthRatio, $heightRatio);
        
        return [
            'width' => (int)round($width * $ratio),
            'height' => (int)round($height * $ratio)
        ];
    }
    
    /**
     * 寻找最优的压缩质量
     */
    private function findOptimalQuality($image, int $type, string $path, int $initialQuality): int
    {
        $quality = $initialQuality;
        $tempPath = $path . '.tmp';
        $minQuality = $this->config['min_quality'];
        $qualityStep = $this->config['quality_step'];
        $targetSize = $this->config['target_size'];
        
        // 从初始质量开始，逐步降低质量直到文件大小符合要求
        while ($quality >= $minQuality) {
            $this->saveImage($image, $type, $tempPath, $quality);
            
            if (file_exists($tempPath)) {
                $size = filesize($tempPath);
                if ($size <= $targetSize) {
                    unlink($tempPath);
                    return $quality;
                }
                unlink($tempPath);
            }
            
            $quality -= $qualityStep;
        }
        
        // 如果质量降到最低还是太大，返回最低质量
        return $minQuality;
    }
    
    /**
     * 保存图片
     */
    private function saveImage($image, int $type, string $path, int $quality): bool
    {
        switch ($type) {
            case IMAGETYPE_JPEG:
                return imagejpeg($image, $path, $quality);
            case IMAGETYPE_PNG:
                // PNG的质量参数范围是0-9，需要转换
                $pngQuality = (int)round((100 - $quality) / 10);
                return imagepng($image, $path, $pngQuality);
            case IMAGETYPE_GIF:
                return imagegif($image, $path);
            case IMAGETYPE_WEBP:
                return imagewebp($image, $path, $quality);
            default:
                return false;
        }
    }
    
    /**
     * 批量压缩图片
     */
    public function batchCompress(array $imagePaths, array $options = []): array
    {
        $results = [];
        $defaultOptions = [
            'quality' => $this->config['default_quality'],
            'maxWidth' => $this->config['max_width'],
            'maxHeight' => $this->config['max_height']
        ];
        
        $options = array_merge($defaultOptions, $options);
        
        foreach ($imagePaths as $path) {
            $results[$path] = $this->compressImage(
                $path,
                null,
                $options['quality'],
                $options['maxWidth'],
                $options['maxHeight']
            );
        }
        
        return $results;
    }
    
    /**
     * 检查图片是否需要压缩
     */
    public function needsCompression(string $path): bool
    {
        if (!$this->config['enable']) {
            return false;
        }
        
        $info = $this->getImageInfo($path);
        if (!$info) {
            return false;
        }
        
        return $info['size'] > $this->config['target_size'] || 
               $info['width'] > $this->config['max_width'] || 
               $info['height'] > $this->config['max_height'];
    }
    
    /**
     * 获取推荐的压缩参数
     */
    public function getRecommendedSettings(string $path): array
    {
        $info = $this->getImageInfo($path);
        if (!$info) {
            return [
                'quality' => $this->config['default_quality'],
                'maxWidth' => $this->config['max_width'],
                'maxHeight' => $this->config['max_height']
            ];
        }
        
        // 根据文件大小确定质量
        $quality = $this->config['default_quality'];
        foreach ($this->config['quality_strategy'] as $size => $recommendedQuality) {
            if ($info['size'] > $size) {
                $quality = $recommendedQuality;
                break;
            }
        }
        
        // 根据图片尺寸确定最大尺寸
        $maxWidth = $this->config['max_width'];
        $maxHeight = $this->config['max_height'];
        $maxDimension = max($info['width'], $info['height']);
        
        foreach ($this->config['dimension_strategy'] as $dimension => $limits) {
            if ($maxDimension > $dimension) {
                $maxWidth = $limits[0];
                $maxHeight = $limits[1];
                break;
            }
        }
        
        return [
            'quality' => $quality,
            'maxWidth' => $maxWidth,
            'maxHeight' => $maxHeight
        ];
    }
    
    /**
     * 记录日志
     */
    private function log(string $message, array $context = [], string $level = 'info')
    {
        if (!$this->config['log']['enable']) {
            return;
        }
        
        $logLevel = $this->config['log']['level'];
        if ($level === 'error' || $logLevel === 'info') {
            logs($message, $context,$level);
        }
    }
    
    /**
     * 获取压缩统计信息
     */
    public function getCompressionStats(string $path): array
    {
        $info = $this->getImageInfo($path);
        if (!$info) {
            return [];
        }
        
        $needsCompression = $this->needsCompression($path);
        $settings = $this->getRecommendedSettings($path);
        
        return [
            'file_size' => $info['size'],
            'file_size_mb' => round($info['size'] / (1024 * 1024), 2),
            'dimensions' => $info['width'] . 'x' . $info['height'],
            'needs_compression' => $needsCompression,
            'recommended_settings' => $settings,
            'target_size' => $this->config['target_size'],
            'target_size_mb' => round($this->config['target_size'] / (1024 * 1024), 2)
        ];
    }
}
